package org.f0rb.demo.dao;

import org.hibernate.LockMode;
import org.hibernate.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;


/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 2010-1-28
 * Time: 11:42:51
 */
public abstract class HibernateDAOImpl<T> extends HibernateDaoSupport implements HibernateDAO<T> {
    private static final Logger LOGGER = LoggerFactory.getLogger(HibernateDAOImpl.class);
    protected String tableName;
    protected String entityName;
    protected Class<T> entityClass;

    @SuppressWarnings("unchecked")
    public HibernateDAOImpl() {
        Class<T> clazz = (Class<T>) getClass();
        assert clazz != HibernateDAOImpl.class : "AbstractModel是抽象类";
        while (!clazz.getSuperclass().equals(HibernateDAOImpl.class)) {
            clazz = (Class<T>) clazz.getSuperclass();
        }
        Type type = clazz.getGenericSuperclass();
        assert (type instanceof ParameterizedType);
        entityClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
        tableName = entityClass.getSimpleName();
        entityName = entityClass.getName();
    }

    public HibernateDAOImpl(Class<T> clazz) {
        entityClass = clazz;
        entityName = entityClass.getName();
        tableName = entityClass.getSimpleName();
    }

    public Serializable save(T t) {
        LOGGER.info("saving " + t.getClass().getName() + " instance");
        Serializable id = getHibernateTemplate().save(t);
        LOGGER.info("save successful");
        return id;
    }

    public void delete(T t) {
        LOGGER.info("deleting " + entityName + " instance");
        getHibernateTemplate().delete(t);
        LOGGER.info("delete successful");
    }

    public T merge(T t) {
        LOGGER.info("merging {} instance", entityName);
        T result = getHibernateTemplate().merge(t);
        LOGGER.info("merge successful");
        return result;
    }

    public void update(T t) {
        LOGGER.info("updating {} instance", entityName);
        getHibernateTemplate().update(t);
        LOGGER.info("update successful");
    }

    public void attachDirty(T instance) {
        LOGGER.info("attaching dirty {} instance", entityName);
        getHibernateTemplate().saveOrUpdate(instance);
        LOGGER.info("attach successful");
    }

    public void attachClean(T instance) {
        LOGGER.info("attaching clean {} instance", entityName);
        getHibernateTemplate().lock(instance, LockMode.NONE);
        LOGGER.info("attach successful");
    }

    public T get(Integer id) {
        LOGGER.info("getting " + entityName + " instance with id: " + id);
        T instance = getHibernateTemplate().get(entityClass, id);
        LOGGER.info("get successful");
        return instance;
    }

    public T load(Integer id) {
        LOGGER.info("loading " + entityName + " instance with id: " + id);
        T instance = getHibernateTemplate().load(entityClass, id);
        LOGGER.info("load successful");
        return instance;
    }

    @SuppressWarnings("unchecked")
    public T uniqueByProperty(final String propertyName, final Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + propertyName
                + ", value: " + value);
        StringBuilder querySB = new StringBuilder();
        querySB.append("from ").append(tableName).append(" where ").append(propertyName).append("=?");
        Query query = getSession().createQuery(querySB.toString());
        query.setParameter(0, value);
        return (T) query.uniqueResult();
    }

    @SuppressWarnings("unchecked")
    public List<T> listByProperty(String propertyName, Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + propertyName
                + ", value: " + value);
        StringBuilder querySB = new StringBuilder();
        querySB.append("from ").append(tableName).append(" where ").append(propertyName).append("=?");
        return getHibernateTemplate().find(querySB.toString(), value);
    }

    public List findByNamedQuery(String queryName) {
        return findByNamedQuery(queryName, new Object[]{});
    }

    public List findByNamedQuery(String queryName, Object value) {
        return findByNamedQuery(queryName, new Object[]{value});
    }

    public List findByNamedQuery(String queryName, Object[] values) {
        LOGGER.info("finding with NamedQuery: " + queryName);
        return getHibernateTemplate().findByNamedQuery(queryName, values);
    }

    public int updateByNamedQuery(String queryName) {
        return updateByNamedQuery(queryName, new Object[0]);
    }

    public int updateByNamedQuery(String queryName, Object value) {
        return updateByNamedQuery(queryName, new Object[]{value});
    }

    /**
     * 该方法出现问题的话参照 getHibernateTemplate().findByNamedQuery 修改
     *
     * @see org.springframework.orm.hibernate3.HibernateTemplate#findByNamedQuery
     */
    public int updateByNamedQuery(final String queryName, final Object[] values) {
        LOGGER.info("updating with NamedQuery: " + queryName);
        Query query = getSession().getNamedQuery(queryName);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.executeUpdate();
    }

    public List pageByNamedQuery(String queryName, int start, int limit) {
        return listPartByNamedQuery(queryName, new Object[0], start, limit);
    }

    public List pageByNamedQuery(String queryName, Object value, int start, int limit) {
        return listPartByNamedQuery(queryName, new Object[]{value}, start, limit);
    }

    @SuppressWarnings("unchecked")
    public List<T> listPartByNamedQuery(String queryName, Object[] values, int start, int limit) {
        LOGGER.info("finding with NamedQuery: " + queryName
                + " and paging with start: " + start + ", limit: " + limit);
        Query query = getSession().getNamedQuery(queryName);
        if (values != null) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        if (start > 0) {
            query.setFirstResult(start);
        }
        if (limit > 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }

    public List listColumns(String... columnNames) {
        StringBuffer columns = new StringBuffer();
        if (columnNames.length > 0) {
            columns.append(columnNames[0]);
            for (int i = 1; i < columnNames.length; i++) {
                columns.append(", ").append(columnNames[i]);
            }
        }
        LOGGER.info("finding column[{}] of {} instance", columns, entityName);
        StringBuilder queryString = new StringBuilder();
        if (columnNames.length > 0) {
            queryString.append("select ").append(columns);
        }
        queryString.append(" from ").append(tableName);
        return getHibernateTemplate().find(queryString.toString());
    }

    @SuppressWarnings("unchecked")
    public List<T> page(int start, int limit) {
        LOGGER.info("finding " + limit + " " + entityName + " instances from the " + (start + 1) + "(th) instance");
        return pageByProperty(null, null, start, limit);
    }

    @SuppressWarnings("unchecked")
    public List<T> pageByProperty(String propertyName, Object value, int start, int limit) {
        StringBuilder queryString = new StringBuilder();
        queryString.append("from ").append(tableName);
        if (propertyName != null && value != null) {
            LOGGER.info("finding " + limit + " " + entityName + " instances from "
                    + (start + 1) + "(th) instance with property: " + propertyName
                    + ", value: " + value);
            queryString.append(" where ").append(propertyName).append("=?");
        }
        Query query = getSession().createQuery(queryString.toString());
        if (value != null) {
            query.setParameter(0, value);
        }
        if (start > 0) {
            query.setFirstResult(start);
        }
        if (limit > 0) {
            query.setMaxResults(limit);
        }
        return query.list();
    }

    @SuppressWarnings("unchecked")
    public List<T> listAll() {
        LOGGER.info("finding all " + entityName + " instances");
        StringBuilder querySB = new StringBuilder();
        querySB.append("from ").append(tableName);
        return getHibernateTemplate().find(querySB.toString());
    }

    public final Long sizeByPropertyInTable(String cTableName, String propertyName, Object value) {
        LOGGER.info("check if exists with property: " + propertyName
                + ", value: " + value + "in table:" + cTableName);
        StringBuilder querySB = new StringBuilder();
        querySB.append("select count(*) from ").append(cTableName).append(" where ").append(propertyName).append("=?");
        return (Long) getHibernateTemplate().find(querySB.toString(), value).get(0);
    }

    public Object getByIdInTable(String tableName, Object id) {
        LOGGER.info("check if exists id[" + id + "] in table:" + tableName);
        StringBuilder querySB = new StringBuilder();
        querySB.append("from ").append(tableName).append(" where id=?");
        Query query = getSession().createQuery(querySB.toString());
        query.setParameter(0, id);
        return query.uniqueResult();
    }

    public Long countByProperty(String key, Object value) {
        LOGGER.info("finding " + entityName + " instance with property: " + key + ", value: " + value);
        StringBuilder querySB = new StringBuilder();
        querySB.append("select count(*) from ").append(tableName).append(" where ").append(key).append("= ?");
        return (Long) getHibernateTemplate().find(querySB.toString(), value).get(0);
    }

    public int size() {
        return count().intValue();
    }

    public Long count() {
        LOGGER.info("finding total results of " + entityName + " instances");
        StringBuilder querySB = new StringBuilder();
        querySB.append("select count(*) from ").append(tableName);
        return (Long) getHibernateTemplate().find(querySB.toString()).get(0);
    }
}
